#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2023, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
# -------------------------------------------------------------------------- #

ONE_LOCATION = ENV['ONE_LOCATION']

if !ONE_LOCATION
    RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
    GEMS_LOCATION     = '/usr/share/one/gems'
else
    RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
    GEMS_LOCATION     = ONE_LOCATION + '/share/gems'
end

# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
    real_gems_path = File.realpath(GEMS_LOCATION)
    if !defined?(Gem) || Gem.path != [real_gems_path]
        $LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }

        # Suppress warnings from Rubygems
        # https://github.com/OpenNebula/one/issues/5379
        begin
            verb = $VERBOSE
            $VERBOSE = nil
            require 'rubygems'
            Gem.use_paths(real_gems_path)
        ensure
            $VERBOSE = verb
        end
    end
end
# %%RUBYGEMS_SETUP_END%%

$LOAD_PATH << RUBY_LIB_LOCATION

require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'rexml/document'
require 'nokogiri'
require 'digest/md5'

require_relative '../common/lxd'

#-------------------------------------------------------------------------------
#  This class is used to query the TurnkeyLinux repository
#-------------------------------------------------------------------------------
class TurnkeyLinux

    #---------------------------------------------------------------------------
    # Default Configuration parameters for the Driver
    #---------------------------------------------------------------------------
    DEFAULTS = {
        :url        => 'http://mirror.turnkeylinux.org',
        :platform   => '/turnkeylinux/images/proxmox/',
        :sizemb     => 5120,
        :fs         => 'ext4',
        :format     => 'raw',
        :agent      => 'OpenNebula',
        :memory     => '768',
        :cpu        => 1,
        :vcpu       => 2,
        :privileged => true
    }

    #---------------------------------------------------------------------------
    # Configuration varibales
    #   :url of linuxcontainers market place
    #   :sizemb default size for container images
    #   :fs filesystem for the image file
    #   :format for the image file, qcow2, raw
    #   :agent for HTTP client
    #---------------------------------------------------------------------------
    def initialize(options = {})
        @options = DEFAULTS
        @options.merge!(options)

        vp = File.dirname(__FILE__) + '/../../VERSION'

        @options[:agent] = "OpenNebula #{File.read(vp)}" if File.exist? vp
    end

    #---------------------------------------------------------------------------
    # Fetch URL
    #---------------------------------------------------------------------------
    def get(path)
        # Get proxy params (needed for ruby 1.9.3)
        http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']

        p_host  = nil
        p_port  = nil

        if http_proxy
            p_uri   = URI(http_proxy)
            p_host  = p_uri.host
            p_port  = p_uri.port
        end

        uri = URI(@options[:url] + path)
        req = Net::HTTP::Get.new(uri.request_uri)

        req['User-Agent'] = @options[:agent] if @options[:agent]

        rc = Net::HTTP.start(uri.hostname, uri.port, p_host, p_port) do |http|
            http.request(req)
        end

        return [rc.code.to_i, rc.msg] unless rc.is_a? Net::HTTPSuccess

        [0, rc.body]
    end

    #---------------------------------------------------------------------------
    # Get appliance list
    #---------------------------------------------------------------------------
    def appliances
        try    = 0
        appstr = ''
        apps   = []

        loop do
            rc, body = get(@options[:platform])

            return [rc, body] if rc != 0

            parsed = Nokogiri::HTML.parse(body)
            apps   = parsed.css('pre').text.lines

            break unless apps.empty?

            try += 1

            return [0, ''] if try > 5
        end

        # Line example
        # debian-9-turnkey-zurmo_15.2-1_amd64.tar.gz.hash 2018-11-29 09:45  2.2K
        apps[0..-1].each do |l|
            f = l.split

            next if f[0] =~ /\.hash$/

            image = f[0].split('-')
            time  = f[1].split('-')
            regt  = Time.new(time[0], time[1], time[2]).to_i

            next if image[1].to_i < 9

            data = {
                'NAME'        => "#{image[3].strip} - LXD",
                'SOURCE'      => app_url(f[0]),
                'IMPORT_ID'   => -1,
                'ORIGIN_ID'   => -1,
                'TYPE'        => 'IMAGE',
                'PUBLISHER'   => 'turnkeylinux.org',
                'MD5'         => md5(regt),
                'FORMAT'      => 'raw',
                'VERSION'     => '1.0',
                'TAGS'        => '',
                'REGTIME'     => regt,
                'SIZE'        => @options[:sizemb],
                'DESCRIPTION' => "Based on #{image[0]}-#{image[1]}",
                'LINK'        => "#{@options[:url]}/#{@options[:platform]}"
            }

            tmpl = ''
            data.each {|key, val| print_var(tmpl, key, val) }

            tmpl64 = ''
            print_var(tmpl64, 'DRIVER', 'raw')

            data = { 'APPTEMPLATE64' => tmpl64,
                     'VMTEMPLATE64' => LXDMarket.template(@options) }
            data.each do |key, val|
                print_var(tmpl, key, Base64.strict_encode64(val))
            end

            appstr << "APP=\"#{Base64.strict_encode64(tmpl)}\"\n"
        end

        [0, appstr]
    end

    def app_url(path)
        "lxd://#{@options[:url]}/#{@options[:platform]}/#{path}?" \
        "size=#{@options[:sizemb]}&filesystem=#{@options[:fs]}&" \
        "format=#{@options[:format]}"
    end

    def print_var(str, name, val)
        return if val.nil?
        return if val.class == String && val.empty?

        str << "#{name}=\"#{val}\"\n"
    end

    # Returns an md5 from a combination of the @option hash and an input string
    def md5(string)
        Digest::MD5.hexdigest("#{@options} #{string}")
    end

end

################################################################################
# Main Program. Outpust the list of marketplace appliances
################################################################################
def set_option(opt, doc, name, path)
    opt[name] = doc.elements[path].text if doc.elements[path]
end

begin
    options     = {}
    drv_message = Base64.decode64(ARGV[0])

    doc = REXML::Document.new(drv_message).root

    pre = 'MARKETPLACE/TEMPLATE'

    data = {
        :url           => "#{pre}/ENDPOINT",
        :sizemb        => "#{pre}/IMAGE_SIZE_MB",
        :fs            => "#{pre}/FILESYSTEM",
        :format        => "#{pre}/FORMAT",
        :memory        => "#{pre}/MEMORY",
        :cpu           => "#{pre}/CPU",
        :vcpu          => "#{pre}/VCPU",
        :privileged    => "#{pre}/PRIVILEGED"
    }

    data.each {|key, value| set_option(options, doc, key, value) }

    rc, str = TurnkeyLinux.new(options).appliances

    if rc != 0
        STDERR.puts str
        exit(-1)
    end

    puts str
end
